package com.practice.WebSocket.Handler;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.CharsetUtil;

import java.util.Date;

import static io.netty.handler.codec.http.HttpHeaders.isKeepAlive;
import static io.netty.handler.codec.http.HttpHeaders.setContentLength;


public class WebSocketServerHandler extends
        SimpleChannelInboundHandler<Object> {
    WebSocketServerHandshaker handshaker;

    @Override
    protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof FullHttpRequest) {
            handleHttpRequest(ctx, (FullHttpRequest) msg);
        } else if (msg instanceof WebSocketFrame) {
            handleHttpFrame(ctx, (WebSocketFrame) msg);
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest msg) {
        if (!msg.getDecoderResult().isSuccess()
                || !"websocket".equals(msg.headers().get
                ("Upgrade"))) {
            sendHttpResponse(ctx, msg, new
                    DefaultFullHttpResponse(HttpVersion
                    .HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
            return;
        }
        WebSocketServerHandshakerFactory factory = new
                WebSocketServerHandshakerFactory
                ("ws://localhost:8080/websocket", null,
                        false);
        handshaker = factory.newHandshaker(msg);
        if (handshaker == null) {
            WebSocketServerHandshakerFactory
                    .sendUnsupportedWebSocketVersionResponse(ctx.channel());
        } else {
            handshaker.handshake(ctx.channel(), msg);
        }
    }

    private void handleHttpFrame(ChannelHandlerContext ctx, WebSocketFrame msg) {
        if (msg instanceof CloseWebSocketFrame) {
            handshaker.close(ctx.channel(), (CloseWebSocketFrame) msg.retain());
        }
        if (msg instanceof PingWebSocketFrame) {
            ctx.channel().write(new PongWebSocketFrame
                    (msg.content().retain()));
            return;
        }
        if (!(msg instanceof TextWebSocketFrame)) {
            throw new UnsupportedOperationException
                    (String.format("%s frame not " +
                            "supported", msg.getClass()
                            .getName()));
        }
        String request = ((TextWebSocketFrame) msg).text();
        ctx.channel().write(new TextWebSocketFrame
                (request + "欢迎使用Netty WebSocket 现在时刻是:" + new
                        Date().toString()));
    }

    private void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest msg,
                                  FullHttpResponse defaultHttpResponse) {
        if (defaultHttpResponse.getStatus().code() != 200) {
            ByteBuf buf = Unpooled.copiedBuffer
                    (defaultHttpResponse.getStatus()
                            .toString(), CharsetUtil
                            .UTF_8);
            defaultHttpResponse.content().writeBytes(buf);
            buf.release();
            setContentLength(defaultHttpResponse,
                    defaultHttpResponse
                            .content().readableBytes());
            ChannelFuture f = ctx.channel().writeAndFlush
                    (defaultHttpResponse);
            if (!isKeepAlive(msg) || defaultHttpResponse
                    .getStatus().code() != 200) {
                f.addListener(ChannelFutureListener.CLOSE);
            }
        }
    }
}
